/*-------------------------------------------------------------------------
 *
 * nodeForeignscan.c
 *	  支持外部表扫描的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeForeignscan.c
 *
 *-------------------------------------------------------------------------
 */
/* 
 * 接口例程
 * 
 *		ExecForeignScan			扫描外部表。
 *		ExecInitForeignScan		创建并初始化状态信息。
 *		ExecReScanForeignScan		重新扫描外部关系。
 *		ExecEndForeignScan		释放任何已分配的资源。
 */
#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeForeignscan.h"
#include "foreign/fdwapi.h"
#include "utils/memutils.h"
#include "utils/rel.h"

static TupleTableSlot *fc_ForeignNext(ForeignScanState *fc_node);
static bool fc_ForeignRecheck(ForeignScanState *fc_node, TupleTableSlot *fc_slot);


/* ----------------------------------------------------------------
 *		ForeignNext
 *
 *		这是 ExecForeignScan 的一个工作马
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ForeignNext(ForeignScanState *fc_node)
{
	TupleTableSlot *fc_slot;
	ForeignScan *fc_plan = (ForeignScan *) fc_node->ss.ps.plan;
	ExprContext *fc_econtext = fc_node->ss.ps.ps_ExprContext;
	MemoryContext fc_oldcontext;

	/* 在短暂的上下文中调用 Iterate 函数 */
	fc_oldcontext = MemoryContextSwitchTo(fc_econtext->ecxt_per_tuple_memory);
	if (fc_plan->operation != CMD_SELECT)
	{
		/* 
		 * 直接修改不能被重新评估，因此在 EvalPlanQual 处理期间不应到达此处
		 */
		Assert(fc_node->ss.ps.state->es_epq_active == NULL);

		fc_slot = fc_node->fdwroutine->IterateDirectModify(fc_node);
	}
	else
		fc_slot = fc_node->fdwroutine->IterateForeignScan(fc_node);
	MemoryContextSwitchTo(fc_oldcontext);

	/* 
	 * 将有效值插入 tableoid，这是唯一有实际用的系统
	 * 列。
	 */
	if (fc_plan->fsSystemCol && !TupIsNull(fc_slot))
		fc_slot->tts_tableOid = RelationGetRelid(fc_node->ss.ss_currentRelation);

	return fc_slot;
}

/* 
 * ForeignRecheck -- 访问方法例程，以重新检查 EvalPlanQual 中的元组
 */
static bool fc_ForeignRecheck(ForeignScanState *fc_node, TupleTableSlot *fc_slot)
{
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;
	ExprContext *fc_econtext;

	/* 
	 * 从外部扫描节点提取必要的信息
	 */
	fc_econtext = fc_node->ss.ps.ps_ExprContext;

	/* 该元组是否满足远程条件？ */
	fc_econtext->ecxt_scantuple = fc_slot;

	ResetExprContext(fc_econtext);

	/* 
	 * 如果外部连接被下推，RecheckForeignScan 可能需要在槽中存储
	 * 不同的元组，因为不同的列集合可能在重新检查时变为 NULL。
	 * 否则，它不应该需要更改槽的内容，只需返回 true 或 false 以指示
	 * 条件是否仍然通过。对于简单情况，设置 fdw_recheck_quals
	 * 可能比提供此回调更容易。
	 */
	if (fc_fdwroutine->RecheckForeignScan &&
		!fc_fdwroutine->RecheckForeignScan(fc_node, fc_slot))
		return false;

	return ExecQual(fc_node->fdw_recheck_quals, fc_econtext);
}

/* ----------------------------------------------------------------
 *		ExecForeignScan(node)
 *
 *		从 FDW 获取下一个元组，检查本地条件，并
 *		返回它。
 *		我们调用 ExecScan() 函数并将适当的
 *		访问方法函数传递给它。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecForeignScan(PlanState *fc_pstate)
{
	ForeignScanState *fc_node = castNode(ForeignScanState, fc_pstate);
	ForeignScan *fc_plan = (ForeignScan *) fc_node->ss.ps.plan;
	EState	   *fc_estate = fc_node->ss.ps.state;

	/* 
	 * 在 EvalPlanQual 激活时忽略直接修改 --- 这些对 EvalPlanQual
	 * 重新检查是无关的
	 */
	if (fc_estate->es_epq_active != NULL && fc_plan->operation != CMD_SELECT)
		return NULL;

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_ForeignNext,
					(ExecScanRecheckMtd) fc_ForeignRecheck);
}


/* ----------------------------------------------------------------
 *		ExecInitForeignScan
 * ----------------------------------------------------------------
 */
ForeignScanState *
ExecInitForeignScan(ForeignScan *fc_node, EState *fc_estate, int fc_eflags)
{
	ForeignScanState *fc_scanstate;
	Relation	fc_currentRelation = NULL;
	Index		fc_scanrelid = fc_node->scan.scanrelid;
	int			fc_tlistvarno;
	FdwRoutine *fc_fdwroutine;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * 创建状态结构
	 */
	fc_scanstate = makeNode(ForeignScanState);
	fc_scanstate->ss.ps.plan = (Plan *) fc_node;
	fc_scanstate->ss.ps.state = fc_estate;
	fc_scanstate->ss.ps.ExecProcNode = fc_ExecForeignScan;

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_scanstate->ss.ps);

	/* 
	 * 打开扫描关系（如果有的话）；还从 FDW 的处理程序获取函数指针
	 */
	if (fc_scanrelid > 0)
	{
		fc_currentRelation = ExecOpenScanRelation(fc_estate, fc_scanrelid, fc_eflags);
		fc_scanstate->ss.ss_currentRelation = fc_currentRelation;
		fc_fdwroutine = GetFdwRoutineForRelation(fc_currentRelation, true);
	}
	else
	{
		/* 我们不能使用 relcache，因此以其他方式获取 fdwroutine */
		fc_fdwroutine = GetFdwRoutineByServerId(fc_node->fs_server);
	}

	/* 
	 * 确定扫描元组的类型。如果 FDW 提供了描述扫描元组的目标列表，
	 * 则使用该目标列表；否则使用基本关系的行类型。
	 */
	if (fc_node->fdw_scan_tlist != NIL || fc_currentRelation == NULL)
	{
		TupleDesc	fc_scan_tupdesc;

		fc_scan_tupdesc = ExecTypeFromTL(fc_node->fdw_scan_tlist);
		ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss, fc_scan_tupdesc,
							  &TTSOpsHeapTuple);
		/* 节点的目标列表将包含varno = INDEX_VAR的Vars */
		fc_tlistvarno = INDEX_VAR;
	}
	else
	{
		TupleDesc	fc_scan_tupdesc;

		/* 不要相信 FDW 返回满足 NOT NULL 约束的元组 */
		fc_scan_tupdesc = CreateTupleDescCopy(RelationGetDescr(fc_currentRelation));
		ExecInitScanTupleSlot(fc_estate, &fc_scanstate->ss, fc_scan_tupdesc,
							  &TTSOpsHeapTuple);
		/* 节点的目标列表将包含varno = scanrelid的Vars */
		fc_tlistvarno = fc_scanrelid;
	}

	/* 不知道 FDW 可能返回什么 */
	fc_scanstate->ss.ps.scanopsfixed = false;
	fc_scanstate->ss.ps.scanopsset = true;

	/*
	 * 初始化结果槽、类型和投影。
	 */
	ExecInitResultTypeTL(&fc_scanstate->ss.ps);
	ExecAssignScanProjectionInfoWithVarno(&fc_scanstate->ss, fc_tlistvarno);

	/*
	 * 初始化子表达式
	 */
	fc_scanstate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_scanstate);
	fc_scanstate->fdw_recheck_quals =
		ExecInitQual(fc_node->fdw_recheck_quals, (PlanState *) fc_scanstate);

	/* 
	 * 确定是否异步扫描外部关系；
	 * 必须与 ExecInitAppend() 中的代码保持同步。
	 */
	fc_scanstate->ss.ps.async_capable = (((Plan *) fc_node)->async_capable &&
									  fc_estate->es_epq_active == NULL);

	/* 
	 * 初始化与 FDW 相关的状态。
	 */
	fc_scanstate->fdwroutine = fc_fdwroutine;
	fc_scanstate->fdw_state = NULL;

	/* 
	 * 为了 FDW 的方便，查找修改目标关系的
	 * ResultRelInfo。ModifyTable 节点应已为我们初始化它，
	 * 见 ExecInitModifyTable。
	 * 
	 * 但在 EvalPlanQual 激活时，不要尝试查找 ResultRelInfo。
	 * 直接修改不能作为 EvalPlanQual 的一部分重新评估。
	 * 此查找无论如何都无效，因为在 EvalPlanQual 处理期间，
	 * EvalPlanQual 仅初始化 ModifyTable 之下的子树，
	 * 而不运行 ExecInitModifyTable。
	 */
	if (fc_node->resultRelation > 0 && fc_estate->es_epq_active == NULL)
	{
		if (fc_estate->es_result_relations == NULL ||
			fc_estate->es_result_relations[fc_node->resultRelation - 1] == NULL)
		{
			elog(ERROR, "result relation not initialized");
		}
		fc_scanstate->resultRelInfo = fc_estate->es_result_relations[fc_node->resultRelation - 1];
	}

	/* 初始化任何外部计划。 */
	if (outerPlan(fc_node))
		outerPlanState(fc_scanstate) =
			ExecInitNode(outerPlan(fc_node), fc_estate, fc_eflags);

	/* 
	 * 告诉 FDW 初始化扫描。
	 */
	if (fc_node->operation != CMD_SELECT)
	{
		/* 
		 * 直接修改不能被 EvalPlanQual 重新评估，因此
		 * 不要费心准备 FDW。
		 * 
		 * 在目标外部的更新/删除情况下，EvalPlanQual 子树中可能
		 * 存在直接修改的 ForeignScan 节点，因此我们在 EvalPlanQual
		 * 处理期间需要忽略这些 ForeignScan 节点。
		 * 另见 ExecForeignScan/ExecReScanForeignScan。
		 */
		if (fc_estate->es_epq_active == NULL)
			fc_fdwroutine->BeginDirectModify(fc_scanstate, fc_eflags);
	}
	else
		fc_fdwroutine->BeginForeignScan(fc_scanstate, fc_eflags);

	return fc_scanstate;
}

/* ----------------------------------------------------------------
 *		ExecEndForeignScan
 *
 *		释放通过 C 例程分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndForeignScan(ForeignScanState *fc_node)
{
	ForeignScan *fc_plan = (ForeignScan *) fc_node->ss.ps.plan;
	EState	   *fc_estate = fc_node->ss.ps.state;

	/* 让 FDW 关闭 */
	if (fc_plan->operation != CMD_SELECT)
	{
		if (fc_estate->es_epq_active == NULL)
			fc_node->fdwroutine->EndDirectModify(fc_node);
	}
	else
		fc_node->fdwroutine->EndForeignScan(fc_node);

	/* 关闭任何外部计划。 */
	if (outerPlanState(fc_node))
		ExecEndNode(outerPlanState(fc_node));

	/* 释放 exprcontext */
	ExecFreeExprContext(&fc_node->ss.ps);

	/* 清空元组表 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);
}

/* ----------------------------------------------------------------
 *		ExecReScanForeignScan
 *
 *		重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanForeignScan(ForeignScanState *fc_node)
{
	ForeignScan *fc_plan = (ForeignScan *) fc_node->ss.ps.plan;
	EState	   *fc_estate = fc_node->ss.ps.state;
	PlanState  *fc_outerPlan = outerPlanState(fc_node);

	/* 
	 * 在 EvalPlanQual 激活时忽略直接修改 --- 这些对 EvalPlanQual
	 * 重新检查是无关的
	 */
	if (fc_estate->es_epq_active != NULL && fc_plan->operation != CMD_SELECT)
		return;

	fc_node->fdwroutine->ReScanForeignScan(fc_node);

	/*
	 * 如果子节点的chgParam不为null，则计划将通过第一个ExecProcNode重新扫描。外部计划也可能为NULL，在这种情况下根本没有什么可以重新扫描的。
	 */
	if (fc_outerPlan != NULL && fc_outerPlan->chgParam == NULL)
		ExecReScan(fc_outerPlan);

	ExecScanReScan(&fc_node->ss);
}

/* ----------------------------------------------------------------
 *		ExecForeignScanEstimate
 *
 *		告知并行协调信息的大小（如果有的话）
 * ----------------------------------------------------------------
 */
void ExecForeignScanEstimate(ForeignScanState *fc_node, ParallelContext *fc_pcxt)
{
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	if (fc_fdwroutine->EstimateDSMForeignScan)
	{
		fc_node->pscan_len = fc_fdwroutine->EstimateDSMForeignScan(fc_node, fc_pcxt);
		shm_toc_estimate_chunk(&fc_pcxt->estimator, fc_node->pscan_len);
		shm_toc_estimate_keys(&fc_pcxt->estimator, 1);
	}
}

/* ----------------------------------------------------------------
 *		ExecForeignScanInitializeDSM
 *
 *		初始化并行协调信息
 * ----------------------------------------------------------------
 */
void ExecForeignScanInitializeDSM(ForeignScanState *fc_node, ParallelContext *fc_pcxt)
{
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	if (fc_fdwroutine->InitializeDSMForeignScan)
	{
		int			fc_plan_node_id = fc_node->ss.ps.plan->plan_node_id;
		void	   *fc_coordinate;

		fc_coordinate = shm_toc_allocate(fc_pcxt->toc, fc_node->pscan_len);
		fc_fdwroutine->InitializeDSMForeignScan(fc_node, fc_pcxt, fc_coordinate);
		shm_toc_insert(fc_pcxt->toc, fc_plan_node_id, fc_coordinate);
	}
}

/* ----------------------------------------------------------------
 *		ExecForeignScanReInitializeDSM
 *
 *		在开始新的扫描之前重置共享状态。
 * ----------------------------------------------------------------
 */
void ExecForeignScanReInitializeDSM(ForeignScanState *fc_node, ParallelContext *fc_pcxt)
{
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	if (fc_fdwroutine->ReInitializeDSMForeignScan)
	{
		int			fc_plan_node_id = fc_node->ss.ps.plan->plan_node_id;
		void	   *fc_coordinate;

		fc_coordinate = shm_toc_lookup(fc_pcxt->toc, fc_plan_node_id, false);
		fc_fdwroutine->ReInitializeDSMForeignScan(fc_node, fc_pcxt, fc_coordinate);
	}
}

/* ----------------------------------------------------------------
 *		ExecForeignScanInitializeWorker
 *
 *		根据并行协调信息进行初始化
 * ----------------------------------------------------------------
 */
void ExecForeignScanInitializeWorker(ForeignScanState *fc_node,
								ParallelWorkerContext *fc_pwcxt)
{
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	if (fc_fdwroutine->InitializeWorkerForeignScan)
	{
		int			fc_plan_node_id = fc_node->ss.ps.plan->plan_node_id;
		void	   *fc_coordinate;

		fc_coordinate = shm_toc_lookup(fc_pwcxt->toc, fc_plan_node_id, false);
		fc_fdwroutine->InitializeWorkerForeignScan(fc_node, fc_pwcxt->toc, fc_coordinate);
	}
}

/* ----------------------------------------------------------------
 *		ExecShutdownForeignScan
 *
 *		给予FDW机会停止异步资源消耗并释放仍然持有的任何资源。
 * ----------------------------------------------------------------
 */
void ExecShutdownForeignScan(ForeignScanState *fc_node)
{
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	if (fc_fdwroutine->ShutdownForeignScan)
		fc_fdwroutine->ShutdownForeignScan(fc_node);
}

/* ----------------------------------------------------------------
 *		ExecAsyncForeignScanRequest
 *
 *		异步请求来自指定异步能力节点的元组
 * ----------------------------------------------------------------
 */
void ExecAsyncForeignScanRequest(AsyncRequest *fc_areq)
{
	ForeignScanState *fc_node = (ForeignScanState *) fc_areq->requestee;
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	Assert(fc_fdwroutine->ForeignAsyncRequest != NULL);
	fc_fdwroutine->ForeignAsyncRequest(fc_areq);
}

/* ----------------------------------------------------------------
 *		ExecAsyncForeignScanConfigureWait
 *
 *		在异步模式下，配置等待
 * ----------------------------------------------------------------
 */
void ExecAsyncForeignScanConfigureWait(AsyncRequest *fc_areq)
{
	ForeignScanState *fc_node = (ForeignScanState *) fc_areq->requestee;
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	Assert(fc_fdwroutine->ForeignAsyncConfigureWait != NULL);
	fc_fdwroutine->ForeignAsyncConfigureWait(fc_areq);
}

/* ----------------------------------------------------------------
 *		ExecAsyncForeignScanNotify
 *
 *		在相关事件发生时调用的回调
 * ----------------------------------------------------------------
 */
void ExecAsyncForeignScanNotify(AsyncRequest *fc_areq)
{
	ForeignScanState *fc_node = (ForeignScanState *) fc_areq->requestee;
	FdwRoutine *fc_fdwroutine = fc_node->fdwroutine;

	Assert(fc_fdwroutine->ForeignAsyncNotify != NULL);
	fc_fdwroutine->ForeignAsyncNotify(fc_areq);
}
